2022
Jan
01
這篇文章說明如果 Mock private Method ,幫助你減化 unit test 範圍,進而提升寫測試的意願與讓測試碼更好維護。
假如你想測試 class 中的 Method A ,而這個 Method A 分別又去 call Method B / C ,為了降低測試的範圍,我們會希望 Mock Method B 和 C 的 return 值 (mock 是指模擬物件的行為
)。
B / C 如果是 public 或是 protected method ,我們只要使用 mockito
or spock
就能輕鬆做 到 mock,困難的是如果想 mock 的對象是 private method , private method 只有 object 自已可以 access ,其它 test framework 都沒辦法對 private method 下手,目前唯有 PowerMock
,它直接修改了 java class byte-code ,把 private 改成 public accessible,讓測試碼能直接操作 private method 。
Example
- Method A
- - Call private Method B
- - Call private Method C
powerMock 使用範例
- 我有一個 class App.java,其中 log 是 public , logInternal 是我想 mock 的 private method
App.java
- package com;
- public class App {
- public App() {
- }
- public String log() {
- System.out.println("run log");
- return this.logInternal();
- }
- private String logInternal() {
- System.out.println("run logInternal");
- return "internal";
- }
- }
- class AppTest.java: 需要使用 PowerMock 中的 createPartialMock / expectPrivate / replay 這 3 個
AppTest.java
- package com;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import static org.junit.Assert.assertEquals;
- import org.powermock.api.easymock.PowerMock;
- import org.powermock.core.classloader.annotations.PrepareForTest;
- import org.powermock.modules.junit4.PowerMockRunner;
- @RunWith(PowerMockRunner.class)
- @PrepareForTest(App.class)
- public class AppTest {
- @Test
- public void TestLog() {
- try {
- App app = PowerMock.createPartialMock(App.class, "logInternal");
- PowerMock.expectPrivate(app, "logInternal").andReturn("mock_result");
- PowerMock.replay(app);
- String ret = app.log();
- assertEquals("Result is " + ret, ret, "mock_result");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- pom.xml dependency
Example
- <dependency>
- <groupId>org.powermock</groupId>
- <artifactId>powermock-module-junit4</artifactId>
- <version>2.0.9</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.powermock</groupId>
- <artifactId>powermock-api-mockito2</artifactId>
- <version>2.0.9</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.powermock</groupId>
- <artifactId>powermock-api-easymock</artifactId>
- <version>2.0.9</version>
- <scope>test</scope>
- </dependency>
build.gradle
使用 gradle 的話,build config 可以參考這份
Example
- plugins {
- id "application"
- id "java"
- }
- application {
- mainClassName = 'com.App'
- }
- allprojects {
- repositories {
- mavenCentral()
- }
- }
- dependencies {
- def powerMockVersion="2.0.9"
- implementation (
- "org.powermock:powermock-module-junit4:${powerMockVersion}",
- "org.powermock:powermock-api-mockito2:${powerMockVersion}",
- "org.powermock:powermock-api-easymock:${powerMockVersion}"
- )
- }